Skip to content

Visual editor

These sheets can be edited in the browser with Elin ModMaker. A character's sprites, portrait, talk lines, dramas and per-language text are gathered in one place, and cross-sheet references such as race, job and elements come with completion and validation.

Drama โ€‹

A drama is a rich dialog that usually has options and additional actions.

Routing โ€‹

To give a character a default drama, place id.xlsx in LangMod/**/Dialog/Drama/, using the character's ID as the file name (e.g., tinymita.xlsx for tinymita). The game automatically loads the drama file whose name matches the character ID.

To use a differently named drama file, such as DramaFileId.xlsx, also add the tag addDrama(DramaFileId) to the character's source tag column.

You can also use the C# API chara.SetDramaOverride(DramaFileId) or chara.ShowDialog(DramaId, step).

Refer to the game sheets in Elin/Package/_Elona/Lang/_Dialog/Drama when making new drama sheets, or download the Tiny Mita example for a template.

Hot Reload

Drama sheets can be edited during gameplay. Changes apply automatically next time the dialog opens.

Definition โ€‹

Line โ€‹

A drama sheet is read top to bottom and consists of multiple lines. Each line has these fields (defined in the first row):

  • step: Marks the start of a step; all following lines belong to this step until another step appears.
  • jump: The step to jump to after this line runs.
  • if / if2: Conditions to check before execution. If if2 is present, both must be true.
  • action: The action to perform.
  • param: Parameter for the action.
  • actor: The speaker for the action or talk line.
    • ?: ???.
    • tg: Target drama character. Default when actor is blank.
    • narrator: Default narrator.
    • pc: Player.
  • id: Unique ID, required for text and choice lines. Not needed for other lines.
  • text_XX / text_JP / text_EN / text: Dialogue content. XX is a language code (e.g., text_CN, text_ZHTW). text is the fallback if a non-builtin language is missing. text_JP and text_EN are required, but you don't have to provide translations.

(Click to zoom) img

Step โ€‹

Drama flow is organized into steps. Each step contains one or many lines, which can include dialogue, actions, and conditions.

main is the default starting step, and end exits the drama.

Avoid step names starting with _ or flag; they can conflict with internal steps.

Builtin Steps โ€‹

Builtin Steps

The builtin drama steps below are always injected when the drama loads. To use one, set it as the jump target. The inject/Unique action also inserts the default talk options ("Let's Talk" etc.) that reference these steps, so you usually do not need to use them yourself.

step nameusage
_banishEnd the drama
_byeEnd the drama
_toggleSharedEquipToggle tg shared equipment usage
_daMakeMaidSet tg as maid
_joinPartyIf tg trait is joinable, set as party member. Not the same as inviting
_leavePartyRemove tg from party and send to home zone
_makeLivestockSet tg as faction livestock
_makeResidentSet tg as faction resident
_departRemove tg from faction
_rumorRumor
_sleepBesideToggle tg sleep beside player state
_disableLoyalToggle tg loyal state
_sucktg sucks pc. Blood sucking takes priority over cat huffing
_insultToggle tg taunt state
_makeHomeSet current zone branch as tg home branch
_inviteTry to invite tg as an ally; checks player attributes and whether tg can be invited. To unconditionally invite as ally, use expanded action join_party()
_GuideGuide player to a list of locations
_tailHave sex, for money
_whoreHave sex, cost money
_bloomDeepen bond with tg
_buyBuy stuff from tg
_buyPlanBuy research plan from tg
_giveGive stuff to tg
_blessingBlessing upon party
_trainTrain skills with tg
_changeDomainChange tg domain
_reviveRevive dead allies
_buySlaveBuy slave from tg
_tradeTrade items with tg
_identifyIdentify items with tg
_identifyAllIdentify all items with tg
_identifySPIdentify items with tg using superior skill
_boutChallenge to a duel
_newsSpawn a random dungeon on map
_healHeal the player
_foodBuy some food from tg
_depositDeposit with tg
_withdrawWithdraw with tg
_copyItemDuplicate item with tg
_extraTaxPay additional tax
_upgradeHearthUpgrade hearth stone
_sellFameSell fame
_investZoneInvest current zone
_investShopInvest tg barter shop
_changeTitleChange player title
_buyLandExpand current zone map
_disableMoveSet tg to not move
_enableMoveSet tg can move

Actions โ€‹

Text lines are the most common. They only have id and text columns, plus an optional if condition. When executed, text lines require player input (click or key press) to advance.

Action lines (except choice) execute automatically without input. If both action and text are present, text is generally ignored.

For example, an action line placed after a text line won't execute until the text line is clicked to advance.

Builtin Actions โ€‹

Builtin Actions
actionparamdescription
injectUniqueInsert "Let's Talk" and a lot of useful steps
choiceAdd a choice to the last text line. Requires text and jump
choice/byeInsert a default bye choice
cancelSet right click / escape key behavior. Requires jump, usually set to end
setFlagflag name,value(optional)Set a flag with value or default 1 if not provided
reloadReload the drama so any flag changes made in the current drama can be applied. jump is optional and usually set to main; when omitted, execution continues in place. Don't confuse this with hot reload during development: for that you only need to save the changes, and the drama is reloaded the next time you start it
enableToneEnable dialog tone for the entire drama
addActorAdd a drama actor to use later; text can set a name override. This happens automatically when you put a new id in the actor cell. Requires character id in actor
invokemethod nameCall a method. All available methods are hardcoded for specific uses.
setBGimage name(optional)Set an image as the background, or leave empty to clear it. Images can come from the Texture folder
BGMBGM idSwitch the BGM to specific one by id. Check the Sound & BGM page for custom BGM
stopBGMStop the BGM and do not continue
lastBGMStop the BGM and continue the last one played
soundsound idPlay a sound by id. Check the Sound & BGM page for custom sounds
waitdurationPause execution at this line for the given number of seconds; useful for letting an animation finish
alphaIn alphaOutdurationAlpha (transparency) transition in seconds
alphaInOutduration,wait timealphaIn first, wait in seconds, then alphaOut
fadeIn fadeOutduration,white/black(optional)Fade transition in seconds
fadeInOutduration,wait time,white/black(optional)fadeIn first, wait in seconds, then fadeOut
hideUItransitionHide the HUD elements with a transition in seconds. Restored when exiting drama
hideDialogHide the drama dialog for cutscenes. Text lines force the dialog to show again, so combine this with wait
endExplicitly end the drama. Same as jump to drama step end
addKeyItemkeyitem idAdd keyitem with id to the player
dropitem idDrop an item as reward at player's position
addResourceresource name,countAdd home resource by count
shakeShake the screen
slapThe drama owner character slaps the player (executes after the dialog closes, player takes minor damage)
destroyItemitem idFind and destroy the item with id from player's inventory
focusImmediately move and focus camera to the drama owner character
focusCharacharacter id,speed(optional)Move and focus camera to the character with id on the same map
focusPCspeed(optional)Move and focus camera to the player
unfocusReset and unfocus camera
destroycharacter idDestroy a character with id on the same map
saveSave game
setHourhourSet the game time in hours

Separate multiple parameters with commas (,) and no spaces in between.

Text โ€‹

Text in the text_JP, text_EN and text_XX columns becomes a talk event; the player must click or press a key to continue. You cannot combine action lines with text lines unless the action says otherwise.

Random Topic โ€‹

$topic picks a random line from a defined topic in chara_talk.xlsx, either from the Elin default file at Package/_Elona/Lang/EN/Data/chara_talk.xlsx or from LangMod/**/Data/chara_talk.xlsx. For example, $sup plays one of the following lines at random:

What?
Huh?
...?
Did you need something?
What do you want?
What's up?
Is something wrong?
What is it?

Substitutions โ€‹

textvalue
#tg_histg's possessive pronoun
#tg_himtg's object pronoun
#tgtg character name
#last_choicetext of the last choice
#newlinenewline character
#costHirecost to hire tg (number, localized)
#self / #mefull name of character tg (including title)
#histg's possessive pronoun
#himtg's object pronoun
#1 ~ #5external variables refDrama1 ~ refDrama5 (number auto-formatted)
#godname of the god (if tg is not empty, uses its faith; otherwise random religion)
#player / #titleplayer's title
#zonecurrent zone name
#guild_titletitle of the guild associated with the current plot
#guildname of the guild in the current plot
#raceplayer's race name
#pcplayer's short name
#pc_fullplayer's full name (including title)
#pc_hisplayer's possessive pronoun
#pc_himplayer's object pronoun
#pc_raceplayer's race
#akaplayer's alias
#bigdaddylocalized string "bigdaddy"
#festivalfestival name (if the zone has a festival, use its name; otherwise generic)
#brother2"brother" or "sister" (based on player gender)
#brotherrandom term for brother/sister (randomly from bro or sis list)
#onii2random term for older brother/sister (from onii2/onee2 lists)
#oniirandom term for older brother/sister (from onii/onee lists)
#genderterm for player gender (indexed into gendersDrama list by gender)
#he"he" or "she" (based on player gender)
#Hesame as above, but with initial capital letter

These substitutions can also be used in dialog.xlsx.

Dynamic Content โ€‹

A text cell that starts with #eval <C# script here..> is evaluated dynamically; the script must return a string.

Conditions โ€‹

Conditions decide whether a line runs.

Static Conditions โ€‹

Static conditions are evaluated once on load.

Attach conditions to any line using the if (and optionally if2) column.

ConditionParamDescription
hasFlagflag namePlayer has flag and value โ‰  0
!hasFlagflag namePlayer doesn't have flag or value = 0
hasMelilithCurseโ€”Player has Melilith curse
merchantโ€”Player is at Merchant Guild
fighterโ€”Player is at Fighter Guild
thiefโ€”Player is at Thief Guild
mageโ€”Player is at Mage Guild
hasItemitem idPlayer has the item in inventory
isCompletedquest idPlayer has completed the quest

Simple value checks (most common for flags/counters):

=,example_flag,1
>,example_counter,20
!,example_flag,69

Most lines only need the if column. Add an if2 column if you need multiple conditions.

Dynamic Conditions โ€‹

To enable/disable a line dynamically, use:

  • invoke* condition expression, or
  • eval action that returns a bool

Branching โ€‹

Set jump to a drama step to branch into that step.

Set jump to eval_result and use an eval action that returns a string to dynamically choose the jump target.

Invoke* Expression โ€‹

The special action invoke* (or i* for short) calls expansion methods:

Syntax โ€‹

Set action to invoke* or i*, and param to one of the valid methods:

actionparamactor
invoke*/i*honk_honk(arg1, arg2)pc

This invokes a method called honk_honk with 2 parameters, arg1 and arg2.

Shorthand prefixes are available for condition composition: !expr = not(expr), &expr = and(expr), ?expr = or(expr).

Parameter โ€‹

Parameters go inside the method's parentheses, separated by , , as in code. With no parameters, use empty () parentheses.

Most methods use the actor cell as the target character, such as pc, tg (the drama target character), or any valid character id. Default is tg.

If the line's jump has a value, the method's return value decides whether the jump happens: true jumps, anything else does not.

Value Expression Syntax โ€‹

DramaValueExpression is used to evaluate or assign values.

Examples:

ExpressionMeaning
69Assign value 69
=69Assign value 69
+5Increase original value by 5
-3Decrease original value by 3
*10Multiply original value by 10
/2Divide original value by 2
==69Check if equal to 69
!=114Check if not equal to 114
>10Check if greater than 10
>=20Check if greater than or equal to 20
<5Check if less than 5
<=3Check if less than or equal to 3
++Increase original value by 1
--Decrease original value by 1
x10Multiply original value by 10 (alias of *)

Expanded Actions โ€‹

methodparamdescriptionjump
add_itemitem id, material alias(optional), level(optional), count(optional)Add the item with id to actor. Defaults to the item's default material, auto level, and a count of 1always
equip_itemitem idCreate and equip the item with id on actoralways
destroy_itemitem id, count(optional)Destroy items with id on actor, default -1 which destroys all matching itemsalways
join_faithreligion id(optional)Make actor join the religion with id, or leave the current religion if the id is emptyalways
join_partyMake actor join player party unconditionallyalways
apply_conditioncondition alias, powerApply a condition with id to actor, default power 100always
remove_conditioncondition aliasRemove the condition on actoralways

Expanded Scene Play โ€‹

methodparamdescriptionjump
move_next_tocharacter idMove actor next to the character with id on the same mapIf target found
move_tilex offset, y offsetMove actor with the relative tile offset, such as 1, 1 or 2, -1always
move_tox, yMove actor to the absolute tile position on the map, such as 64, 44 or 12, 0always
move_zonezone id, level(optional)Move actor to a specific zone with id, and specific level, default level 0if success
move_zone_2zone full nameMove actor to a specific zone using zone full name syntax, such as derphy@-1if success
play_animeanime idPlay animation on actoralways
play_effecteffect idPlay effect on actoralways
play_effect_ateffect id, x, yPlay effect at tile position on the mapalways
play_emoteemote id, duration(optional)Play emote on actor, default duration 1 secondalways
play_screen_effectscreen effect idPlay screen effectalways
pop_texttextPop a text bubble above actor headalways
set_portraitportrait id(optional)Set actor portrait used during dialog to a specific one or reset with empty id, from Portrait folder, e.g. UN_myChara_happy.png could be set with happy or UN_myChara_happyIf actor found
set_portrait_overrideportrait id(optional)Set actor portrait used outside of dialog to a specific one or reset with empty id, from Portrait folder. Must be full id. This does not affect the current dialog portrait (set by set_portrait)always
set_spritetexture id(optional)Set the custom sprite override for actor or reset with empty id, from Texture folderalways
show_bookcategory/book idOpen a book, supports LangMod/**/Text folder, for example, use Book/ok for Text/Book/ok.txtIf success

Expanded Modifications โ€‹

methodparamdescriptionjump
mod_affinityvalue expressionModify actor affinity with value expressionalways
mod_currencycurrency type, value expressionModify actor currency with value expression. money money2 plat medal influence casino_coin ecopoalways
mod_elementelement alias, value(optional), potential(optional)Modifies a specified element (feat/resistance/skill, etc.) for the actor, default value 1 and potential 100. Different types of elements use different value scalingalways
mod_element_expelement alias, value expressionModifies the exp of a specified element for the actoralways
mod_famevalue expressionModify player fame with value expressionalways
mod_flagflag, value expression(optional)Modify the flag value from actor with value expression, such as +1, =1, 0, default =1. Works on non-player characters too. These per-character flags are stored separately from the setFlag/hasFlag dialog flagsalways
mod_keyitemkeyitem alias, value expression(optional)Modify player's keyitem value with expression, default =1If success

Expanded Conditions โ€‹

These are also expansion methods called with the invoke* action, but here the return value is what matters.

methodparamdescriptionjump
if_affinityvalue expressionCheck actor affinity with expression, such as <5, >=90, !=0If satisfies
if_conditioncondition alias, value expression(optional)Check if actor has active condition with alias, optionally checking its power with expression, default >=1If active
if_currencycurrency type, value expressionCheck actor currency with value expression. money money2 plat medal influence casino_coin ecopoIf satisfies
if_elementelement alias, value expressionCheck actor element with expressionIf satisfies
if_faithreligion id, reward rank(optional)Check if actor is certain religion and meets reward rank, default >=0If satisfies
if_famevalue expressionCheck player's fame with value expressionIf satisfies
if_flagflag name, value expression(optional)Check actor flag value with expression, such as =5, 1, !=0, default >=1. Reads the same per-character flags as mod_flag, not the setFlag/hasFlag dialog flagsIf satisfies
if_lvvalue expressionCheck actor level with value expressionIf satisfies
if_has_itemitem id, value expression(optional)Checks if actor possesses a quantity of the item that meets the expression, default >=1If satisfies
if_hostilityhostility value expressionChecks if actor meets a specific hostility, such as =Ally or >Enemy. Values in ascending order: Enemy, Neutral, Friend, AllyIf satisfies
if_in_partytrue or false(optional)Check if actor is in player's party, default trueIf satisfies
if_keyitemkeyitem alias, value expression(optional)Check if player has keyitem with expression, default >0If satisfies
if_racerace idCheck if actor is of certain raceIf satisfies
if_tagtagCheck if actor has certain tag defined in Chara rowIf defined
if_zonezone id, level(optional)Check if actor is in certain zone and optionally check levelIf present
if_zone_2zone full nameCheck if actor is in certain zone with zone full name syntax, such as derphy@-1If present

Special Methods โ€‹

methodparamdescriptionjump
console_cmdcmd param1 param2...Run console commandalways
evalC# script or file with <<<path.cs syntaxExecute the C# script. Prefer the eval action over invoke* with eval()If returns true
andand(if_flag(flag1, >0), if_flag(flag2, <0)...)Takes invoke* expressions as parameters and evaluates all of themIf all satisfy
oror(if_race(lich), if_race(snail)...)Takes invoke* expressions as parameters and evaluates all of themIf any satisfies
notnot(if_zone(dungeon), if_zone(field), if_zone(underground)...)Takes invoke* expressions as parameters and evaluates all of themIf none satisfies

API โ€‹

Elin has an API for adding custom expansion methods from your own script DLL.

Action Parser โ€‹

During drama loading, parsers are called for registered non-builtin action lines and create the events for them.

cs
[ElinDramaActionParser("my_action")]
public static bool ExampleParser(DramaManager dm, Dictionary<string, string> line) 
{
    dm.AddEvent(new DramaEventTalk(line["actor"], () => {
        // processing
        return "A text line!";
    }));
    // this line is handled
    return true;
}
// or register manually
CustomDramaExpansion.AddDramaActionParser("my_action", ExampleParser);

Action Invoke โ€‹

Invokes are automatically compiled into invoke* expressions and are called when the line is executed during drama play.

cs
[ElinDramaActionInvoke]
public static bool add_item(DramaManager dm, Dictionary<string, string> line,
                            string itemId,
                            string materialAlias = "wood",
                            int lv = -1,
                            int count = 1)
{
    var chara = dm.GetChara(line["actor"]);

    var mat = sources.materials.alias.TryGetValue(materialAlias, "wood");
    var item = ThingGen.Create(itemId, mat.id, lv).SetNum(count);
    chara.Pick(item);

    return true;
}

[ElinDramaActionInvoke("nodiscard")]
public static bool if_element(DramaManager dm, Dictionary<string, string> line,
                              string elementAlias,
                              DramaValueExpression expr) 
{
    var chara = dm.GetChara(line["actor"]);
    return chara.HasElement(elementAlias) && expr.Compare(chara.Evalue(elementAlias));
}
cs
[ElinDramaActionInvoke]
public static bool console_cmd(DramaManager dm, Dictionary<string, string> line, 
                               params string[] parameters)
{
    string.Join(' ', parameters).EvaluateAsCommand();
    return true;
}

Drama invoke methods must be static and return bool, and their parameters must start with DramaManager dm, Dictionary<string, string> line.

Elin can auto-convert the expression parameters, or pass them as string[]. Auto-convertible types are the builtin data types, DramaValueExpression, and custom types with a static bool TryParse(string, out T) method.

See the CustomDramaExpansion implementation for examples.

Text Styles โ€‹

You can use the following tags to add styles like bold/italic/color to the text.

TagDescriptionExample Usage
<b> </b>Bold<b>Bold Text</b>
<i> </i>Italic<i>Italic Text</i>
<size=...> </size>Font size (pixels)<size=22>Large</size>
<color=...> </color>Text color (color name/#hex)<color=red>Red</color> <color=#add8e6ff>Light Blue</color>

In drama sheet text, a line break entered with Alt + Enter shows as a new line on the same page. dialog.xlsx behaves differently.

You can also use #newline to break lines.

For full details, see the Unity Rich Text documentation.

Scripting โ€‹

You can run C# code directly in a drama sheet using the eval action.

It offers the same scripting capabilities as regular C#, with these differences:

  • Script state is tied to the current drama instance (persists until the drama ends, then auto-resets).
  • Shortcuts: dm = DramaManager, line = current line Dictionary<string, string>, tg = target Chara, pc = player Chara.

Return value behavior:

  • bool + valid jump target โ†’ determines whether to jump.
  • string + jump cell set to eval_result โ†’ uses the string as the new jump target.
  • No return value โ†’ treated as a simple action.

Import a script file from the same folder with: <<<script_snippet.cs

Passing Variables โ€‹

Use the shared Script dictionary:

cs
// First eval
var value = EClass.rnd(100) * 5;
Script["random_value"] = value;

// Later eval
var value = (int)Script["random_value"];

Common Examples โ€‹

FunctionCode
Jump to stepdm.Goto("my_new_step");
Add "Let's chat!" optiondm.InjectUniqueRumor();
Add temporary talkdm.AddTempTalk("text", "actor", "jump");
Get Chara instancevar chara = dm.GetChara("tg");
Recruit to partychara.MakeAlly();
Modify levelchara.SetLv(chara.LV + 5);

Need help? Ask on Elona Discord: @freshcloth or reach via email.

This project is an unofficial documentation site and is not affiliated with, endorsed by, or associated with Elin or Lafrontier / Noa. All trademarks are the property of their respective owners.